#include "igmp_pkt_process.h"

#include "../register_init_cfg/register_init.h"
#include "../timer_func/timer_func.h"

extern multicast_context g_multicast_context_;

// 查询报文处理
int igmp_query_pkt_process(u16 node, u8 port)
{
  node_multicast *temp_node_info = g_multicast_context_.node_multicast_context[node];

  // 输入端口不为路由接口
  if (!((temp_node_info->route_port_info.dynamic_route_port >> port) & 1))
  {
    temp_node_info->route_port_info.dynamic_route_port |= (1 << port); // 添加该接口

    update_port(node);
    // 创建动态路由老化计时器
    temp_node_info->route_port_info.aging_time_info.port_aging_time_info[port] =
        (aging_time_cnt_entry *)malloc(sizeof(aging_time_cnt_entry));
  }

  // 更新老化时间
  temp_node_info->route_port_info.aging_time_info.port_aging_time_info[port]->time_cnt = get_cur_nano_sec();

  return 0;
}

// 报告报文处理
int igmp_report_or_join_pkt_process(u16 node, u8 port, u8 *ip,
                                    igmp_pkt *igmp_pkt_, u16 pkt_len)
{
  int j = 0;
  int table_hit = 0;
  node_multicast *temp_node_info = NULL;
  multicast_table_entry *temp_table_info = NULL;

  temp_node_info = g_multicast_context_.node_multicast_context[node];

  // 遍历动态组播表项
  for (j = temp_node_info->multicast_table_info.static_multicast_entry_size;
       j < MAX_MULTICAST_ENTRY_NUM; j++)
  {
    temp_table_info = temp_node_info->multicast_table_info.table[j];

    // 表项不存在
    if (temp_table_info == NULL)
      continue;

    // 无效表项
    if (temp_table_info->valid == 0)
      continue;

    table_hit = !memcmp(temp_table_info->ip, ip, 4);

    // 转发表命中
    if (table_hit)
    {
      // 端口未启用
      if (!((temp_table_info->port >> port) & 1))
      {
        temp_table_info->port |= (1 << port); // 更新该端口
        update_table(node, j);
        // 创建老化计时器
        temp_table_info->aging_time_info.port_aging_time_info[port] =
            (aging_time_cnt_entry *)malloc(sizeof(aging_time_cnt_entry));
      }
      // 更新计时器
      temp_table_info->aging_time_info.port_aging_time_info[port]->time_cnt = get_cur_nano_sec();
      break;
    }
  }

  // 转发表未命中
  if (!table_hit)
  {
    // 遍历动态组播表项
    for (j = temp_node_info->multicast_table_info.static_multicast_entry_size;
         j < MAX_MULTICAST_ENTRY_NUM; j++)
    {
      temp_table_info = temp_node_info->multicast_table_info.table[j];

      // 表项不存在
      if (temp_table_info == NULL)
      {
        // 新增表项
        temp_table_info =
            (multicast_table_entry *)malloc(sizeof(multicast_table_entry));
        bzero(temp_table_info, sizeof(multicast_table_entry));
        temp_node_info->multicast_table_info.table[j] = temp_table_info;
      }

      // 无效表项
      if (temp_table_info->valid == 0)
      {
        temp_table_info->valid = 1;           // 启用该表项
        temp_table_info->type = 0;            // 标记为动态表项
        memcpy(temp_table_info->ip, ip, 4);   // 更新组播IP
        temp_table_info->port |= (1 << port); // 更新该端口

        update_table(node, j);
        // 创建老化计时器
        temp_table_info->aging_time_info.port_aging_time_info[port] =
            (aging_time_cnt_entry *)malloc(sizeof(aging_time_cnt_entry));
        // 更新计时器
        temp_table_info->aging_time_info.port_aging_time_info[port]->time_cnt = get_cur_nano_sec();
        break;
      }
    }
  }

  // 抑制使能开启
  if (temp_node_info
          ->cfg_para_info.report_restrain_enbale)
  {
    u64 restrain_time = get_cur_nano_sec();

    // 响应超时
    if (get_diff(restrain_time, temp_node_info->report_restrain_info.time_cnt) >= (u64)g_multicast_context_.querier_info.querier_maxresptime)
    {
      temp_node_info->report_restrain_info.time_cnt = restrain_time; // 更新超时时间

      u8 tmep_mac[6];
      get_hcp_mac_from_mid(tmep_mac, node);
      tsmp_tunnal_encapsulation(tmep_mac, pkt_len, (u8 *)igmp_pkt_); // 封装隧道帧
    }

    // 未超时直接丢弃报文
  }
  else
  {
    u8 tmep_mac[6];
    get_hcp_mac_from_mid(tmep_mac, node);
    tsmp_tunnal_encapsulation(tmep_mac, pkt_len, (u8 *)igmp_pkt_); // 封装隧道帧
  }
  return 0;
}

// 离组报文处理
int igmp_leave_pkt_process(u16 node, u8 port, u8 *ip, igmp_pkt *igmp_pkt_,
                           u16 pkt_len)
{
  int j = 0;
  int table_hit = 0;
  node_multicast *temp_node_info = NULL;
  multicast_table_entry *temp_table_info = NULL;

  temp_node_info = g_multicast_context_.node_multicast_context[node];

  // 遍历动态组播表项
  for (j = temp_node_info->multicast_table_info.static_multicast_entry_size;
       j < MAX_MULTICAST_ENTRY_NUM; j++)
  {
    temp_table_info = temp_node_info->multicast_table_info.table[j];

    // 表项不存在
    if (temp_table_info == NULL)
      continue;

    // 无效表项
    if (temp_table_info->valid == 0)
      continue;

    table_hit = !memcmp(temp_table_info->ip, ip, 4);

    // 转发表命中
    if (table_hit)
    {
      // 端口已启用
      if ((temp_table_info->port >> port) & 1)
      {
        // 端口为快速离开端口
        if ((temp_node_info->cfg_para_info.quickly_leave_port >> port) & 1)
        {
          temp_table_info->port &= ~(1 << port); // 删除该端口

          update_table(node, j);

          temp_table_info->aging_time_info.port_aging_time_info[port]->time_cnt = 0; // 重置计时器
          break;
        }
      }
    }
  }

  // 抑制使能开启
  if (temp_node_info->cfg_para_info.report_restrain_enbale)
  {
    // 转发表存在更新
    if (j != MAX_MULTICAST_ENTRY_NUM)
    {
      // 组播组无其他成员
      if (temp_table_info->port == 0)
      {
        u8 tmep_mac[6];
        get_hcp_mac_from_mid(tmep_mac, node);
        tsmp_tunnal_encapsulation(tmep_mac, pkt_len, (u8 *)igmp_pkt_); // 封装隧道帧
      }
    }
  }
  else
  {
    u8 tmep_mac[6];
    get_hcp_mac_from_mid(tmep_mac, node);
    tsmp_tunnal_encapsulation(tmep_mac, pkt_len, (u8 *)igmp_pkt_); // 封装隧道帧
  }

  // 离组节点为查询器
  if (g_multicast_context_.querier_info.querier_mid == node)
    igmp_special_pkt_create(ip); // 发送特定组查询报文

  return 0;
}

int igmp_pkt_process(u8 *pkt, u16 pkt_len)
{
  tsmp_header *tsmp_pkt_ = (tsmp_header *)pkt;

  // 首先判断是否为隧道封装报文
  if (TUNNEL_ENCAPSULATION == tsmp_pkt_->type && TUNNEL_ENCAPSULATION_FROM == tsmp_pkt_->sub_type)
  {
    igmp_pkt *igmp_pkt_ = (igmp_pkt *)((u8 *)pkt + sizeof(tsmp_header));

    // 确定为IGMP报文
    if (ntohs(igmp_pkt_->eth_header.eth_type) == 0x0800 && igmp_pkt_->ip_header.protocol == 2)
    {
      igmp_igmp_header *igmp_V2_header = (igmp_igmp_header *)((u8 *)igmp_pkt_ + sizeof(igmp_eth_header) + 4 * (igmp_pkt_->ip_header.ver & 0x0F));
      igmp_V3_header *igmp_V3_header_ = (igmp_V3_header *)((u8 *)igmp_V2_header + sizeof(igmp_igmp_header));

      u16 node = get_mid_from_mac(tsmp_pkt_->smac);

      if (igmp_V2_header->multicast_addr[0] == 224 && igmp_V2_header->multicast_addr[1] == 0 && igmp_V2_header->multicast_addr[2] == 0 && igmp_V2_header->multicast_addr[3] == 251)
      {
        return 0;
      }
      if (igmp_V2_header->multicast_addr[0] == 224 && igmp_V2_header->multicast_addr[1] == 0 && igmp_V2_header->multicast_addr[2] == 0 && igmp_V2_header->multicast_addr[3] == 252)
      {
        return 0;
      }
      if (igmp_V2_header->multicast_addr[0] == 239 && igmp_V2_header->multicast_addr[1] == 255 && igmp_V2_header->multicast_addr[2] == 255 && igmp_V2_header->multicast_addr[3] == 250)
      {
        return 0;
      }

      // 判断IGMP报文类型
      switch (igmp_V2_header->type)
      {
      case 0x11:
        igmp_query_pkt_process(node, igmp_pkt_->eth_header.dmac[0]); //(节点id，输入端口号=dmac最高8bit)
        break;

      case 0x12: // v1：0x12    v2：0x16
      case 0x16:
        igmp_report_or_join_pkt_process(node, igmp_pkt_->eth_header.dmac[0], igmp_V2_header->multicast_addr, igmp_pkt_, pkt_len); //(节点id，输入端口号=dmac最高8bit，组播IP，捕获报文，报文长度)
        break;

      case 0x17:
        igmp_leave_pkt_process(node, igmp_pkt_->eth_header.dmac[0], igmp_V2_header->multicast_addr, igmp_pkt_, pkt_len); //(节点id，输入端口号=dmac最高8bit，组播IP，捕获报文，报文长度)
        break;

      case 0x22: // v3

        // 遍历IGMPV3报文组
        for (int i = 0; i < igmp_V2_header->multicast_addr[2] * 256 + igmp_V2_header->multicast_addr[3]; i++)
        {
          // 报文组为224.xx.xx.xx则跳到下一个报文组
          if (igmp_V3_header_->V3_multicast_addr[0] == 224)
            igmp_V3_header_ = (u8 *)igmp_V3_header_ + 8;
          else
            break;
        }

        // 加入报文
        if (igmp_V3_header_->record_type == 0x04)
          igmp_report_or_join_pkt_process(node, igmp_pkt_->eth_header.dmac[0], igmp_V3_header_->V3_multicast_addr, igmp_pkt_, pkt_len); //(节点id，输入端口号=dmac最高8bit，组播IP，捕获报文，报文长度)

        // 离组报文
        if (igmp_V3_header_->record_type == 0x03)
          igmp_leave_pkt_process(node, igmp_pkt_->eth_header.dmac[0], igmp_V3_header_->V3_multicast_addr, igmp_pkt_, pkt_len); //(节点id，输入端口号=dmac最高8bit，组播IP，捕获报文，报文长度)
        break;

      default:
        break;
      }
    }
  }
  else if (NET_MANAGE == tsmp_pkt_->type && GET_RES == tsmp_pkt_->sub_type) // 读响应报文
  {
    tsmp_set_req_or_get_res_pkt_data *tmp_data = (tsmp_set_req_or_get_res_pkt_data *)((u8 *)pkt + sizeof(tsmp_header));
    u16 node = get_mid_from_mac(tsmp_pkt_->smac);
    u16 ret = 0;

    if (ntohl(tmp_data->base_addr) == DROUTE_PORTBM) // 动态路由验证
    {
      ret = memcmp((u8 *)(g_multicast_context_.node_multicast_context[node]->check_info.buf) + 100, pkt + sizeof(tsmp_header), 10);
      if (ret == 0) // 校验成功
      {
        // 将寄存器对应的bit位置0
        g_multicast_context_.node_multicast_context[node]->check_info.check_flag &= ~(0x00000001 << 10);
      }
    }
    else // 转发表验证
    {
      u16 table = (ntohl(tmp_data->base_addr) - MULTICAST_TABLE) / 4;
      ret = memcmp((u8 *)(g_multicast_context_.node_multicast_context[node]->check_info.buf) + 10 * table, pkt + sizeof(tsmp_header), 10);
      if (ret == 0) // 校验成功
      {
        // 将寄存器对应的bit位置0
        g_multicast_context_.node_multicast_context[node]->check_info.check_flag &= ~(0x00000001 << table);
      }
    }
  }
  return 0;
}